package org.ling.xuan.action.check;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.StrUtil;
import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.actionSystem.CommonDataKeys;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.vfs.LocalFileSystem;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiCodeBlock;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiManager;
import com.intellij.psi.PsiMethod;
import com.intellij.psi.PsiMethodCallExpression;
import com.intellij.psi.PsiReturnStatement;
import com.intellij.psi.util.PsiTreeUtil;
import org.jetbrains.annotations.NotNull;
import org.ling.xuan.util.FileLogUtil;
import org.ling.xuan.util.LoggerUtil;
import org.ling.xuan.util.LxOptional;

import java.io.File;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;

public class LxPsiCheckAction extends AnAction {
    @Override
    public void actionPerformed(@NotNull AnActionEvent e) {
        Project project = e.getProject();
        String inputStr = Messages.showInputDialog("请输入表达式", "表达式", Messages.getInformationIcon());
        File file;
        if (StrUtil.isNotBlank(inputStr)) {
            String type = inputStr.substring(0, 1);
            inputStr = inputStr.substring(1);
            String[] arr = inputStr.split(";");
            if (arr.length != 2 || StrUtil.isBlank(arr[0]) || StrUtil.isBlank(arr[1])) {
                Messages.showMessageDialog("表达式输入不合法", "提示", Messages.getErrorIcon());
                return;
            }
            String[] arr1 = arr[0].split(":");
            String[] arr2 = arr[1].split(":");
            if (arr1.length != 2 || StrUtil.isBlank(arr1[0]) || StrUtil.isBlank(arr1[1])) {
                Messages.showMessageDialog("表达式输入不合法", "提示", Messages.getErrorIcon());
                return;
            }
            if (arr2.length != 2 || StrUtil.isBlank(arr2[0]) || StrUtil.isBlank(arr2[1])) {
                Messages.showMessageDialog("表达式输入不合法", "提示", Messages.getErrorIcon());
                return;
            }
            if ("1".equals(type)) {
                LoggerUtil.log(project, "扫描调用了{}.{}方法确没有调用{}.{}方法的类", arr1[0], arr1[1], arr2[0], arr2[1]);
            } else if ("2".equals(type)) {
                LoggerUtil.log(project, "扫描调用了{}.{}方法确在调用{}.{}方法之前return的类", arr1[0], arr1[1], arr2[0], arr2[1]);
            } else {
                LoggerUtil.log(project, "未知的处理类型,退出");
            }
            String filePath = LxOptional.of(e, c -> c.getDataContext().getData(CommonDataKeys.VIRTUAL_FILE).getPath()).orElse("");
            if (StrUtil.isNotBlank(filePath)) {
                file = new File(FileLogUtil.logRoot + DateUtil.today() + "/" + FileUtil.getPrefix(filePath) + "-" + System.currentTimeMillis() + ".log");
                FileUtil.mkParentDirs(file);
                if(!file.exists()){
                    FileUtil.touch(file);
                }
                if (!FileUtil.isDirectory(filePath)) {
                    if (filePath.endsWith(".java")) {
                        if ("1".equals(type)) {
                            scanFileHasAndNotHas(file, project, filePath, arr1, arr2);
                        } else if ("2".equals(type)) {
                            scanReturnBetween(file, project, filePath, arr1, arr2);
                        }
                    }
                } else {
                    file = new File(FileLogUtil.logRoot + DateUtil.today() + "/" + FileUtil.getName(filePath) + "-" + System.currentTimeMillis() + ".log");
                    FileUtil.mkParentDirs(file);
                    if(!file.exists()){
                       FileUtil.touch(file);
                    }
                    List<File> files = FileUtil.loopFiles(filePath, f -> f.getName().endsWith(".java"));
                    List<String> paths = files.stream().map(File::getAbsolutePath).collect(Collectors.toList());
                    for (String s : paths) {
                        if ("1".equals(type)) {
                            scanFileHasAndNotHas(file, project, s, arr1, arr2);
                        } else if ("2".equals(type)) {
                            scanReturnBetween(file, project, s, arr1, arr2);
                        }
                    }
                }
            }
        }
    }

    private void scanReturnBetween(File logFile, Project project, String javaFilePath, String[] arr1, String[] arr2) {
        LoggerUtil.log(project, "检查文件{}", javaFilePath);
        VirtualFile virtualFile = LocalFileSystem.getInstance().findFileByPath(javaFilePath);
        if (virtualFile == null) {
            LoggerUtil.log(project, "文件路径{}无效", javaFilePath);
            return; // 
        }

        // 获取 PsiFile 对象
        PsiFile psiFile = PsiManager.getInstance(project).findFile(virtualFile);
        List<PsiMethod> methods = getAllMethods(psiFile);
        for (PsiMethod method : methods) {
            // 获取方法体
            PsiCodeBlock methodBody = method.getBody();
            if (methodBody == null) {
                return;
            }

            boolean beginCalled = false;
            try {
                Collection<PsiElement> elements = PsiTreeUtil.findChildrenOfAnyType(methodBody, PsiElement.class);
                // 遍历方法体中的所有语句
                for (PsiElement element : elements) {
                    if (element instanceof PsiMethodCallExpression) {
                        PsiMethodCallExpression methodCallExpression = (PsiMethodCallExpression) element;
                        // 检查是否调用了 session.beginTransaction()
                        if (isPointCallMethod(methodCallExpression, arr1[0], arr1[1])) {
                            beginCalled = true;
                        }
                        // 检查是否调用了 session.endTransaction()
                        if (isPointCallMethod(methodCallExpression, arr2[0], arr2[1])) {
                            beginCalled = false;
                        }
                    }
                    // 检查是否存在 return 语句
                    if (beginCalled && (element instanceof PsiReturnStatement)) {
                        logMatched(logFile, project, method,javaFilePath);
                        return;
                    }
                }
            } catch (Exception e) {
                logException(logFile, project, method, e);
            }
        }
    }

    private void logMatched(File logFile, Project project, PsiMethod method,String javaFilePath) {
        LoggerUtil.log(project, "--- " + method.getName() + " match start---");
        LoggerUtil.log(project, method.getName());
        LoggerUtil.log(project, "---" + method.getName() + " match end---");
        FileLogUtil.log(logFile, "--- " + method.getName() + " match start---");
        FileLogUtil.log(logFile,"--文件:{},方法:{}",javaFilePath,method.getName());
        FileLogUtil.log(logFile, method.getText());
        FileLogUtil.log(logFile, "---" + method.getName() + " match end---");
    }

    private void scanFileHasAndNotHas(File logFile, Project project, String javaFilePath, String[] arr1, String[] arr2) {
        LoggerUtil.log(project, "检查文件{}", javaFilePath);
        VirtualFile virtualFile = LocalFileSystem.getInstance().findFileByPath(javaFilePath);
        if (virtualFile == null) {
            LoggerUtil.log(project, "文件路径{}无效", javaFilePath);
            return; // 
        }

        // 获取 PsiFile 对象
        PsiFile psiFile = PsiManager.getInstance(project).findFile(virtualFile);
        List<PsiMethod> methods = getAllMethods(psiFile);
        for (PsiMethod method : methods) {
            try {
                if (methodHasCall(method, arr1[0], arr1[1]) && (!methodHasCall(method, arr2[0], arr2[1]))) {
                    logMatched(logFile, project, method,javaFilePath);
                    return;
                }
            } catch (Exception e) {
                logException(logFile, project, method, e);
            }
        }
    }

    private void logException(File logFile, Project project, PsiMethod method, Exception e) {
        FileLogUtil.log(logFile, "解析异常", e);
        LoggerUtil.log(project, "---exception start---");
        LoggerUtil.log(project, method.getName());
        LoggerUtil.log(project, "---exception end---");
    }

    private boolean methodHasCall(PsiMethod method, String className, String methodName) {
        PsiCodeBlock methodBody = method.getBody();
        if (methodBody == null) {
            return false; // 如果方法没有方法体，直接返回 false
        }
        Collection<PsiMethodCallExpression> methodCallExpressions = PsiTreeUtil.findChildrenOfAnyType(methodBody, PsiMethodCallExpression.class);
        for (PsiMethodCallExpression methodCallExpression : methodCallExpressions) {
            if (isPointCallMethod(methodCallExpression, className, methodName)) {
                return true;
            }
        }
        return false;
    }

    public boolean isPointCallMethod(PsiMethodCallExpression methodCallExpression, String className, String methodName) {
        boolean result = false;
        if (methodCallExpression != null) {
            PsiMethod resolvedMethod = methodCallExpression.resolveMethod();
            if (resolvedMethod != null) {
                PsiClass containingClass = resolvedMethod.getContainingClass();
                if (containingClass != null) {
                    String qualifiedName = containingClass.getQualifiedName();
                    String curMethodName = resolvedMethod.getName();
                    // 检查是否是 IDBSession 的 beginTransaction 方法
                    if (className.equals(qualifiedName) &&
                            methodName.equals(curMethodName)) {
                        result = true;
                    }
                }
            }
        }
        return result;
    }

    public static List<PsiMethod> getAllMethods(PsiFile psiFile) {
        List<PsiMethod> methods = new ArrayList<>();
        // 获取所有 Java 类
        PsiClass[] classes = PsiTreeUtil.getChildrenOfType(psiFile, PsiClass.class);
        if (classes != null) {
            for (PsiClass psiClass : classes) {
                // 获取类中的所有方法
                PsiMethod[] classMethods = psiClass.getAllMethods();
                for (PsiMethod method : classMethods) {
                    methods.add(method);
                }
            }
        }
        return methods;
    }
}
